package solver;

import gurobi.GRB;
import gurobi.GRBEnv;
import gurobi.GRBException;
import gurobi.GRBLinExpr;
import gurobi.GRBModel;
import gurobi.GRBVar;

import java.lang.Thread.UncaughtExceptionHandler;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

import model.IncrementalCostCalculator;
import model.IncrementalValidator;
import model.Problem;
import model.Process;
import model.Service;
import model.Solution;
import util.Chronograph;

public class MathSolverLC_CV_Final extends BaseSolver {

	private int numberOfSelectedProcesses;
	private int numberOfEligibleMachinesPerProcess;
	Random my_random; // = new Random(1234567890L);

	public MathSolverLC_CV_Final(Problem problem, Solution initial_solution,
			boolean useGRB) {
		super(problem, initial_solution);
		// this.useGRB = useGRB;
		my_random = getRandom();
	}

	public int solveWithTimeLimit_OLD(int seconds) {
		Chronograph chronos = new Chronograph();
		chronos.reset();
		int round = 1;
		Solution s1 = getInitialSolution().copy();
		Solution s2 = getCurrentSolution().copy();
		LateAcceptanceHeuristicSolver solver2 = new LateAcceptanceHeuristicSolver(
				getProblem(), s1, s2);
		solver2.setLfa(20000);
		// solver2.solve(20);
		Problem.VERBOSE = false;
		setCurrentSolution(solver2.getNewSolution().copy());
		getFullValidator().setSolutionPair(getInitialSolution(), getCurrentSolution());
		getFullValidator().computeCost();

		long best_cost = getFullValidator().getInitialSolutionCost();
		int improvement_counter = 0;
		int time_limit = 5;

		if (getFullValidator().getNewSolutionCost() < best_cost) {
			// System.out
			// .printf(":) BETTER solution found (round=%d, improvement=%d, number of moved processes=%d) %,d (reduced by %,d)\n",
			// round + 1, improvement_counter,
			// getNumberOfMovedProcesses(current_solution),
			// ece.getNewSolutionCost(),
			// best_cost - ece.getNewSolutionCost());
			best_cost = getFullValidator().getNewSolutionCost();
			chronos.lap();
			saveSolution(best_cost, chronos.getElapsedTimeInSeconds(), false);
		}
		boolean solved = true;
		int tot_vars_cbc = 900;
		chronos.lap();
		while (chronos.getElapsedTimeInSeconds() < seconds) {
			System.out
					.printf("###########################  ROUND %d   ###########################\n",
							round);
			long time_left_seconds = seconds
					- chronos.getElapsedTimeInSeconds();
			if (time_left_seconds <= 3)
				break;
			System.out.printf("Time left = %d\n", time_left_seconds);

			if (solved) {
				tot_vars_cbc += 30;
			} else {
				if (tot_vars_cbc > 450)
					tot_vars_cbc -= 150;
			}
			if (my_random.nextInt(100) < 50) {
				if (getProblem().M <= 50) {
					numberOfEligibleMachinesPerProcess = my_random
							.nextInt(getProblem().M / 3) + getProblem().M / 2;
				} else {
					numberOfEligibleMachinesPerProcess = my_random
							.nextInt(getProblem().M / 3) + getProblem().M / 4;
				}
				numberOfSelectedProcesses = tot_vars_cbc
						/ numberOfEligibleMachinesPerProcess;
			} else {
				numberOfSelectedProcesses = my_random.nextInt(20) + 40;
				numberOfEligibleMachinesPerProcess = (tot_vars_cbc - 100)
						/ numberOfSelectedProcesses;
			}

			if (round % 13 == 13) {
				// System.out.println("PA");
				s1 = getInitialSolution().copy();
				s2 = getCurrentSolution().copy();
				solver2 = new LateAcceptanceHeuristicSolver(getProblem(), s1, s2);
				solver2.setLfa(lfa);
				if (lfa < 50000) {
					lfa += 1000;
				}

				solver2.solve(Math.min(10, (int) time_left_seconds));
				Problem.VERBOSE = false;
				setCurrentSolution(solver2.getNewSolution().copy());
				getFullValidator().setSolutionPair(getInitialSolution(), getCurrentSolution());
				getFullValidator().computeCost();
				long current_solution_cost = getFullValidator().getNewSolutionCost();
				if (current_solution_cost < best_cost) {
					System.out
							.printf(":) BETTER solution found (round=%d, improvement=%d, number of moved processes=%d) %,d (reduced by %,d)\n",
									round + 1,
									improvement_counter,
									getNumberOfMovedProcesses(getPotentialSolution()),
									current_solution_cost, best_cost
											- current_solution_cost);

					best_cost = current_solution_cost;
					chronos.lap();
					saveSolution(best_cost, chronos.getElapsedTimeInSeconds(),
							false);
				}
			} else {
				List<Integer> selectedProcesses = strategicallySelectProcessesAndMachines(
						numberOfSelectedProcesses,
						numberOfEligibleMachinesPerProcess);
				long cutoff = computeCutOff(selectedProcesses);
				OrtExtraLeanIPModelBuilder modelBuilder = new OrtExtraLeanIPModelBuilder(
						getProblem(), selectedProcesses, false, Math.min(10,
								time_limit), cutoff);
				modelBuilder.setInitialSolution(getInitialSolution());
				modelBuilder.setCurrentSolution(getCurrentSolution());

				if (getProblem().P == getProblem().S
						|| isAllSelectedProcessesAtDifferentServices(selectedProcesses)) {
					modelBuilder.includeConflictHardConstraints(false);
					modelBuilder.includeSpreadHardConstraints(false);
				}
				if (getProblem().N == 1
						|| isAllSelectedProcessesAtOneNeighborhood(selectedProcesses)) {
					modelBuilder.includeDependencyHardConstraints(false);
				}

				solved = modelBuilder.solve(false);
				// System.out.println("problem solved = " + solved);

				if (solved) {
					setPotentialSolution(modelBuilder.saveToSolution());
					getFullValidator().setSolutionPair(getInitialSolution(), getPotentialSolution());
					getFullValidator().computeCost();
					long potential_solution_cost = getFullValidator().getNewSolutionCost();
					if (potential_solution_cost < best_cost) {
						if (getFullValidator().isFeasible()) {
							improvement_counter++;
							System.out
									.printf(":) BETTER solution found (round=%d, improvement=%d, number of moved processes=%d) %,d (reduced by %,d)\n",
											round + 1,
											improvement_counter,
											getNumberOfMovedProcesses(getPotentialSolution()),
											potential_solution_cost, best_cost
													- potential_solution_cost);
							best_cost = potential_solution_cost;
							setCurrentSolution(getPotentialSolution().copy());
							chronos.lap();
							saveSolution(best_cost,
									chronos.getElapsedTimeInSeconds(), false);
						} else {
							// System.out.println("TRANSIENT????????????? :( ??????????????");
						}
					}
				} else {
					modelBuilder.free();
				}
				modelBuilder = null;
			}
			round++;
			chronos.lap();
			Problem.RANDOM_SEED = getRandom().nextLong();
		}

		return chronos.getElapsedTimeInSeconds();
	}

	public int solveWithTimeLimit(int seconds) {
		Chronograph chronos = new Chronograph();
		chronos.reset();
		int round = 1;
		getFullValidator().setSolutionPair(getInitialSolution(), getCurrentSolution());
		getFullValidator().computeCost();
		long best_cost = getFullValidator().getInitialSolutionCost();
		int improvement_counter = 0;
		int time_limit = 5;
		boolean solved = true;
		int tot_vars_cbc = 900;
		chronos.lap();
		while (chronos.getElapsedTimeInSeconds() < seconds) {
			System.out
					.printf("###########################  ROUND %d   ###########################\n",
							round);
			long time_left_seconds = seconds
					- chronos.getElapsedTimeInSeconds();
			if (time_left_seconds <= 3)
				break;
			System.out.printf("Time left = %d\n", time_left_seconds);

			if (solved) {
				tot_vars_cbc += 30;
			} else {
				if (tot_vars_cbc > 450)
					tot_vars_cbc -= 150;
			}
			if (my_random.nextInt(100) < 50) {
				if (getProblem().M <= 50) {
					numberOfEligibleMachinesPerProcess = my_random
							.nextInt(getProblem().M / 3) + getProblem().M / 2;
				} else {
					numberOfEligibleMachinesPerProcess = my_random
							.nextInt(getProblem().M / 3) + getProblem().M / 4;
				}
				numberOfSelectedProcesses = tot_vars_cbc
						/ numberOfEligibleMachinesPerProcess;
			} else {
				numberOfSelectedProcesses = my_random.nextInt(20) + 40;
				numberOfEligibleMachinesPerProcess = (tot_vars_cbc - 100)
						/ numberOfSelectedProcesses;
			}

			
			System.out.printf("var=%d P=%d M=%d\n", tot_vars_cbc, numberOfSelectedProcesses, numberOfEligibleMachinesPerProcess);
			List<Integer> selectedProcesses = strategicallySelectProcessesAndMachines(
					numberOfSelectedProcesses,
					numberOfEligibleMachinesPerProcess);
			long cutoff = computeCutOff(selectedProcesses);
			OrtExtraLeanIPModelBuilder modelBuilder = new OrtExtraLeanIPModelBuilder(
					getProblem(), selectedProcesses, false,
					Math.min(10, time_limit), cutoff);
			modelBuilder.setInitialSolution(getInitialSolution());
			modelBuilder.setCurrentSolution(getCurrentSolution());

			if (getProblem().P == getProblem().S
					|| isAllSelectedProcessesAtDifferentServices(selectedProcesses)) {
				modelBuilder.includeConflictHardConstraints(false);
				modelBuilder.includeSpreadHardConstraints(false);
			}
			if (getProblem().N == 1
					|| isAllSelectedProcessesAtOneNeighborhood(selectedProcesses)) {
				modelBuilder.includeDependencyHardConstraints(false);
			}

			solved = modelBuilder.solve(false);
			// System.out.println("problem solved = " + solved);

			if (solved) {
				setPotentialSolution(modelBuilder.saveToSolution());
				getFullValidator().setSolutionPair(getInitialSolution(), getPotentialSolution());
				getFullValidator().computeCost();
				long potential_solution_cost = getFullValidator().getNewSolutionCost();
				if (potential_solution_cost < best_cost) {
					if (getFullValidator().isFeasible()) {
						improvement_counter++;
						System.out
								.printf(":) BETTER solution found (round=%d, improvement=%d, number of moved processes=%d) %,d (reduced by %,d)\n",
										round + 1,
										improvement_counter,
										getNumberOfMovedProcesses(getPotentialSolution()),
										potential_solution_cost, best_cost
												- potential_solution_cost);
						best_cost = potential_solution_cost;
						setCurrentSolution(getPotentialSolution().copy());
						chronos.lap();
						saveSolution(best_cost,
								chronos.getElapsedTimeInSeconds(), false);
					} else {
						// System.out.println("TRANSIENT????????????? :( ??????????????");
					}
				}
			}
		}
		round++;
		chronos.lap();
		Problem.RANDOM_SEED = getRandom().nextLong();
		return chronos.getElapsedTimeInSeconds();
	}

	int counter = 0;
	int lfa = 30000;

	private List<Integer> strategicallySelectProcessesAndMachines(int nop,
			int noem) {
		getProblem().clearEligibleMachines();
		nop = nop < getProblem().P ? nop : getProblem().P;
		noem = noem < getProblem().M ? noem : getProblem().M;
		List<Integer> selectedProcesses = null;

		int what = (counter) % 7;
//		what = 4;
		System.out.println(what);
		if (what == 0) {
			selectedProcesses = selectMachinesFromMoreAndLessLoadedPerResourceMachines(
					nop, noem);
		} else if (what == 1) {
			selectedProcesses = randomlySelectOneProcessFromEachServiceAndMachines(
					nop, noem);
		} else if (what == 2) {
			selectedProcesses = selectMachinesFromTheSameNeighborhood(nop, noem);
		} else if (what == 3) {
			selectedProcesses = selectMachinesFromAllMachines(nop, noem);
		} else if (what == 4) {
			selectedProcesses = selectFullRandomProcessesAndMachines(nop, noem);
		} else if (what == 5) {
			selectedProcesses = selectMachinesFromTheSameLocation(nop, noem);
		} else if (what == 6) {
			selectedProcesses = selectMachinesFromMoreAndLessLoadedMachines(
					nop, noem);
		}
		counter++;
		clearConflictConstraints(selectedProcesses);
		return selectedProcesses;
	}

	List<Integer> selectMachinesFromAllMachines(int nop, int noem) {

		List<Integer> selectedProcesses = new ArrayList<Integer>();
		List<Integer> mlist = new ArrayList<Integer>();

		int num = 0;
		int pos = my_random.nextInt(40) + 60;

		int numM = getProblem().M;
		List<Integer> nlist = new ArrayList<Integer>();
		for (int i = 0; i < numM; i++) {
			nlist.add(i);
		}
		Collections.shuffle(nlist, my_random);
		for (int i : nlist) {
			int m_id = i;
			int n = 0;
			for (int p_id : getCurrentSolution().getProcessesForMachine(m_id)) {
				if (my_random.nextInt(100) < pos) {
					selectedProcesses.add(p_id);
					n++;
				}
			}
			// num += current_solution.getProcessesForMachine(m_id).size();
			if (n > 0) {
				num += n;
				mlist.add(m_id);
				if (mlist.size() >= noem) {
					break;
				}
				if (num >= nop) {
					break;
				}
			}
		}
		for (int p_id : selectedProcesses) {
			Process proc = getProblem().getProcesses().get(p_id);
			for (int m_id : mlist) {
				proc.addEligibleMachine(m_id);
			}
		}
		return selectedProcesses;
	}

	List<Integer> selectMachinesFromTheSameNeighborhood(int nop, int noem) {

		List<Integer> selectedProcesses = new ArrayList<Integer>();
		List<Integer> mlist = new ArrayList<Integer>();

		int num = 0;
		int pos = my_random.nextInt(40) + 60;

		int numN = getProblem().N;
		List<Integer> nlist = new ArrayList<Integer>();
		for (int i = 0; i < numN; i++) {
			nlist.add(i);
		}
		Collections.shuffle(nlist, my_random);
		for (int k : nlist) {
			List<Integer> nmlist = new ArrayList<Integer>();
			nmlist.addAll(getProblem().getMachinesIdsForNeighborhoodId(k));
			Collections.shuffle(nmlist, my_random);
			for (int m_id : nmlist) {
				int n = 0;
				for (int p_id : getCurrentSolution().getProcessesForMachine(m_id)) {
					if (my_random.nextInt(100) < pos) {
						selectedProcesses.add(p_id);
						n++;
					}
				}
				// num += current_solution.getProcessesForMachine(m_id).size();
				if (n > 0) {
					num += n;
					mlist.add(m_id);
					if (mlist.size() >= noem) {
						break;
					}
					if (num >= nop) {
						break;
					}
				}
			}
			if (mlist.size() >= noem) {
				break;
			}
			if (num >= nop) {
				break;
			}
		}
		for (int p_id : selectedProcesses) {
			Process proc = getProblem().getProcesses().get(p_id);
			for (int m_id : mlist) {
				proc.addEligibleMachine(m_id);
			}
		}
		return selectedProcesses;
	}

	List<Integer> selectMachinesFromTheSameLocation(int nop, int noem) {

		List<Integer> selectedProcesses = new ArrayList<Integer>();
		List<Integer> mlist = new ArrayList<Integer>();

		int num = 0;
		int pos = my_random.nextInt(40) + 60;

		int numL = getProblem().L;
		List<Integer> llist = new ArrayList<Integer>();
		for (int i = 0; i < numL; i++) {
			llist.add(i);
		}
		Collections.shuffle(llist, my_random);
		for (int k : llist) {
			List<Integer> lmlist = new ArrayList<Integer>();
			lmlist.addAll(getProblem().getMachinesIdsForLocationId(k));
			Collections.shuffle(lmlist, my_random);
			for (int m_id : lmlist) {
				int n = 0;
				for (int p_id : getCurrentSolution().getProcessesForMachine(m_id)) {
					if (my_random.nextInt(100) < pos) {
						selectedProcesses.add(p_id);
						n++;
					}
				}
				// num += current_solution.getProcessesForMachine(m_id).size();
				if (n > 0) {
					num += n;
					mlist.add(m_id);
					if (mlist.size() >= noem) {
						break;
					}
					if (num >= nop) {
						break;
					}
				}
			}
			if (mlist.size() >= noem) {
				break;
			}
			if (num >= nop) {
				break;
			}
		}
		for (int p_id : selectedProcesses) {
			Process proc = getProblem().getProcesses().get(p_id);
			for (int m_id : mlist) {
				proc.addEligibleMachine(m_id);
			}
		}
		return selectedProcesses;
	}

	List<Integer> selectMachinesFromMoreAndLessLoadedMachines(int nop, int noem) {

		List<Integer> selectedProcesses = new ArrayList<Integer>();
		List<Integer> mlist = new ArrayList<Integer>();

		int num = 0;
		int pos = my_random.nextInt(40) + 60;

		getCurrentSolution().refreshLoadCostContribution();
		getCurrentSolution().refreshBalanceCostContribution();
		int[] m_a = new int[getProblem().M];
		long[] cc = new long[getProblem().M];
		int[] ev = new int[getProblem().M];
		for (int i = 0; i < getProblem().M; i++) {
			m_a[i] = i;
			cc[i] = getCurrentSolution().balance_cost_contribution_zipped[i]
					+ getCurrentSolution().load_cost_contribution_zipped[i];
			if (getCurrentSolution().balance_cost_contribution_zipped[i]
					+ getCurrentSolution().load_cost_contribution_zipped[i] == 0) {
				ev[i]++;
			}
		}
		sort(cc, m_a);

		for (int i = 0; i < getProblem().M; i += 2) {
			int m_id1, m_id2;

			if (i + 1 >= getProblem().M - i - 2) {
				break;
			}

			if (my_random.nextInt(100) < 50) {
				m_id1 = m_a[i];
			} else {
				m_id1 = m_a[i + 1];
			}
			if (my_random.nextInt(100) < 50) {
				m_id2 = m_a[getProblem().M - i - 1];
			} else {
				m_id2 = m_a[getProblem().M - i - 2];
			}
			int n = 0;
			for (int p_id : getCurrentSolution().getProcessesForMachine(m_id1)) {
				if (my_random.nextInt(100) < pos) {
					selectedProcesses.add(p_id);
					n++;
				}
			}
			// num += current_solution.getProcessesForMachine(m_id1).size();
			if (n > 0) {
				num += n;
				mlist.add(m_id1);
				if (mlist.size() >= noem) {
					break;
				}
				if (num >= nop) {
					break;
				}
			}
			n = 0;
			for (int p_id : getCurrentSolution().getProcessesForMachine(m_id2)) {
				if (my_random.nextInt(100) < pos) {
					selectedProcesses.add(p_id);
					n++;
				}
			}
			// num += current_solution.getProcessesForMachine(m_id2).size();
			if (n > 0) {
				num += n;
				mlist.add(m_id2);
				if (mlist.size() >= noem) {
					break;
				}
				if (num >= nop) {
					break;
				}
			}
		}
		for (int p_id : selectedProcesses) {
			Process proc = getProblem().getProcesses().get(p_id);
			for (int m_id : mlist) {
				proc.addEligibleMachine(m_id);
			}
		}
		return selectedProcesses;
	}

	List<Integer> selectMachinesFromMoreAndLessLoadedPerResourceMachines(
			int nop, int noem) {

		List<Integer> selectedProcesses = new ArrayList<Integer>();
		List<Integer> mlist = new ArrayList<Integer>();

		int num = 0;
		int pos = my_random.nextInt(40) + 60;

		getCurrentSolution().refreshLoadCostContribution();
		getCurrentSolution().refreshBalanceCostContribution();
		int[] m_a = new int[getProblem().M];
		long[] cc = new long[getProblem().M];
		int[] ev = new int[getProblem().M];
		// ---
		// choose resource
		// ----------------
		int r = my_random.nextInt(getProblem().RE);
		for (int i = 0; i < getProblem().M; i++) {
			m_a[i] = i;
			cc[i] = getCurrentSolution().balance_cost_contribution_unzipped[i][r]
					+ getCurrentSolution().load_cost_contribution_unzipped[i][r];
			if (getCurrentSolution().balance_cost_contribution_unzipped[i][r]
					+ getCurrentSolution().load_cost_contribution_unzipped[i][r] == 0) {
				ev[i]++;
			}
		}
		sort(cc, m_a);

		for (int i = 0; i < getProblem().M; i += 2) {
			int m_id1, m_id2;

			if (i + 1 >= getProblem().M - i - 2) {
				break;
			}

			if (my_random.nextInt(100) < 50) {
				m_id1 = m_a[i];
			} else {
				m_id1 = m_a[i + 1];
			}
			if (my_random.nextInt(100) < 50) {
				m_id2 = m_a[getProblem().M - i - 1];
			} else {
				m_id2 = m_a[getProblem().M - i - 2];
			}
			int n = 0;
			for (int p_id : getCurrentSolution().getProcessesForMachine(m_id1)) {
				if (my_random.nextInt(100) < pos) {
					selectedProcesses.add(p_id);
					n++;
				}
			}
			// num += current_solution.getProcessesForMachine(m_id1).size();
			if (n > 0) {
				num += n;
				mlist.add(m_id1);
				if (mlist.size() >= noem) {
					break;
				}
				if (num >= nop) {
					break;
				}
			}
			n = 0;
			for (int p_id : getCurrentSolution().getProcessesForMachine(m_id2)) {
				if (my_random.nextInt(100) < pos) {
					selectedProcesses.add(p_id);
					n++;
				}
			}
			// num += current_solution.getProcessesForMachine(m_id2).size();
			if (n > 0) {
				num += n;
				mlist.add(m_id2);
				if (mlist.size() >= noem) {
					break;
				}
				if (num >= nop) {
					break;
				}
			}
		}
		for (int p_id : selectedProcesses) {
			Process proc = getProblem().getProcesses().get(p_id);
			for (int m_id : mlist) {
				proc.addEligibleMachine(m_id);
			}
		}
		return selectedProcesses;
	}

	/**
	 * Randomly select one process from each one of nop (nop<=S) services
	 * 
	 * @param nop
	 * @return
	 */
	private List<Integer> randomlySelectOneProcessFromEachServiceAndMachines(
			int nop, int noem) {
		if (nop > getProblem().S) {
			nop = getProblem().S;
		}
		if (noem > getProblem().M) {
			noem = getProblem().M;
		}

		List<Integer> selectedProcesses = new ArrayList<Integer>();
		List<Integer> ballot = new ArrayList<Integer>();
		for (int s_id = 0; s_id < getProblem().S; s_id++) {
			ballot.add(s_id);
		}
		Collections.shuffle(ballot, my_random);
		for (int i = 0; i < nop; i++) {
			Service service = getProblem().getServices().get(ballot.get(i));
			int p_id = service.getRandomProcess(my_random);
			selectedProcesses.add(p_id);
		}

		// add all machines for each selected process
		for (Integer p_id : selectedProcesses) {
			Process proc = getProblem().getProcesses().get(p_id);
			for (int m_id = 0; m_id < getProblem().M; m_id++) {
				proc.addEligibleMachine(m_id);
			}
		}

		// filterMachinesSatisfyingConflictsConstraint(selectedProcesses);
		// filterMachinesSatisfyingSpreadConstraints(selectedProcesses);
		// filterMachinesSatisfyingDependencyConstraints(selectedProcesses);

		// keep |noem| machines at most for each selected process
		for (Integer p_id : selectedProcesses) {
			Process proc = getProblem().getProcesses().get(p_id);
			Collections.shuffle(proc.getEligibleMachines(), my_random);
			int current_machine_id = getCurrentSolution()
					.getMachineIdForProcessId(p_id);
			boolean flag = false;
			while (proc.getEligibleMachines().size() > noem) {
				int m_id = proc.getEligibleMachines().get(0);
				if (m_id == current_machine_id) {
					flag = true;
				}
				proc.getEligibleMachines().remove(0);
			}
			if (flag) {
				proc.addEligibleMachine(current_machine_id);
			}
		}

		return selectedProcesses;
	}

	boolean isAllSelectedProcessesAtDifferentServices(
			List<Integer> selectedProcesses) {
		List<Integer> services = new ArrayList<Integer>();
		for (int p_id : selectedProcesses) {
			int s_id = getProblem().getServiceIdForProcessId(p_id);
			if (services.contains(s_id)) {
				return false;
			} else {
				services.add(s_id);
			}
		}
		return true;
	}

	boolean isAllSelectedProcessesAtOneNeighborhood(
			List<Integer> selectedProcesses) {
		int n = -1;
		for (int p_id : selectedProcesses) {
			Process proc = getProblem().getProcesses().get(p_id);
			for (int m_id : proc.getEligibleMachines()) {
				int n_id = getProblem().getNeighborhoodIdForMachineId(m_id);
				if (n == -1) {
					n = n_id;
				} else if (n_id != n) {
					return false;
				}
			}
		}
		return true;
	}
}
